/*
*
* Copyright (c) 2011 Vaulting Systems International
* 
* Permission is hereby granted, free of charge, to any person obtaining a copy 
* of this software and associated documentation files (the "Software"), to deal 
* in the Software without restriction, including without limitation the rights 
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 
* of the Software, and to permit persons to whom the Software is furnished to do  
* so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all  
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, 
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 
* PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE  
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
*/
package com.ekeymanlib.business;

import java.util.ArrayList;
import java.util.List;

import com.ekeyman.utils.DigitalSignature;
import com.ekeymanlib.dao.AccessAuditDao;
import com.ekeymanlib.dao.AppDeviceDao;
import com.ekeymanlib.dao.ResourceDao;
import com.ekeymanlib.dao.VendorDao;
import com.ekeymanlib.domain.AccessAudit;
import com.ekeymanlib.domain.AppDevice;
import com.ekeymanlib.domain.Resource;
import com.ekeymanlib.dto.EncryptionKeys;
import com.ekeymanlib.dto.ResourceGrid;
import com.ekeymanlib.dto.SearchFilter;
import com.ekeymanlib.util.KeyUtils;

public class ResourceBO {
	
	private VendorDao vendorDao;
	private ResourceDao resourceDao;
	private AppDeviceDao appDeviceDao;
	private AccessAuditDao accessAuditDao;
	
	public ResourceBO(){}

	public EncryptionKeys createResource(String resourceUri,
			String publicKey, int keyBytesLength, int ivBytesLength, int saltBytesLength, 
			int iterationCountMax, String location, String digitalSignature) {
		
		EncryptionKeys encryptionKeys = new EncryptionKeys();
		AccessAudit accessAudit = new AccessAudit();
		
		// lookup private key
		AppDevice appDevice = getAppDeviceDao().getAppDevice(publicKey);
		
		//create computed digital signature from public/private key and data
		String computedDigitalSignature = DigitalSignature.createDigitalSignature(
				resourceUri, publicKey, appDevice.getPrivateKey(), Integer.toString(keyBytesLength), 
				Integer.toString(ivBytesLength), Integer.toString(saltBytesLength), 
				Integer.toString(iterationCountMax), location);
		
		// verify that computed digital signature equals request digital signature
		if(digitalSignature.equals(computedDigitalSignature)){
			
			Resource resource = getResourceDao().getResource(resourceUri, 
					appDevice.getApplication(), appDevice.getDevice(), appDevice.getPublicKey());
			
			// If resource already exists then return it
			if(resource == null){
				resource = new Resource();
				//generate new encryption key
				byte[] newKeyBytes = KeyUtils.generateKey(keyBytesLength);
				byte[] newIv = KeyUtils.generateIv(ivBytesLength);
				byte[] newSalt = KeyUtils.generateSalt(saltBytesLength);
				int newIterationCount = KeyUtils.generateIterationCount(iterationCountMax);
				//store encryption key for resource uri
				
				resource.setKeyBytes(newKeyBytes);
				resource.setKeyBytesLength(keyBytesLength);
				resource.setIvBytes(newIv);
				resource.setIvBytesLength(ivBytesLength);
				resource.setSaltBytes(newSalt);
				resource.setSaltBytesLength(saltBytesLength);
				resource.setIterationCount(newIterationCount);
				resource.setResourceUri(resourceUri);
				resource.setAppDevice(appDevice);
				getResourceDao().saveResource(resource);
			}
			
			populateEncryptionKeys(encryptionKeys, resource);
			
			accessAudit.setAccessResult(AccessAudit.SUCCESS);
		}
		else {
			accessAudit.setAccessResult(AccessAudit.BREACH);
			populateBreachEncryptionKeys(encryptionKeys);
		}
		
		accessAudit.setAccessType("create encryption key");
		accessAudit.setApplication(appDevice.getApplication());
		accessAudit.setDevice(appDevice.getDevice());
		accessAudit.setResourceUri(resourceUri);
		accessAudit.setLocation(location);
		getAccessAuditDao().saveAccessAudit(accessAudit);
		
		return encryptionKeys;
	}
	
	public EncryptionKeys getResource(String resourceUri, 
			String publicKey, String location, String digitalSignature){
		
		EncryptionKeys encryptionKeys = new EncryptionKeys();
		AccessAudit accessAudit = new AccessAudit();
		
		// lookup private key
		AppDevice appDevice = getAppDeviceDao().getAppDevice(publicKey);
		
		//create computed digital signature from public/private key and data
		String computedDigitalSignature = DigitalSignature.createDigitalSignature(
				resourceUri, publicKey, appDevice.getPrivateKey(), location);
		
		// verify that computed digital signature equals request digital signature
		if(digitalSignature.equals(computedDigitalSignature)){
			
			//get encryption key for resource uri
			Resource resource = getResourceDao().getResource(resourceUri, 
					appDevice.getApplication(), appDevice.getDevice(),publicKey);
			if(resource != null) {
				populateEncryptionKeys(encryptionKeys, resource);
				accessAudit.setAccessResult(AccessAudit.SUCCESS);
			}
			else {
				accessAudit.setAccessResult(AccessAudit.NOT_FOUND);
				populateBreachEncryptionKeys(encryptionKeys);
			}
		}
		else {
			accessAudit.setAccessResult(AccessAudit.BREACH);
			populateBreachEncryptionKeys(encryptionKeys);
		}
		
		accessAudit.setAccessType("get encryption key");
		accessAudit.setApplication(appDevice.getApplication());
		accessAudit.setDevice(appDevice.getDevice());
		accessAudit.setResourceUri(resourceUri);
		accessAudit.setLocation(location);
		getAccessAuditDao().saveAccessAudit(accessAudit);

		return encryptionKeys;
		
	}
	
	public long getresourceCount(List<SearchFilter> searchFilters){
		return getResourceDao().getResourceCount(searchFilters);
	}

	
	public void deleteResource(String resourceUri, String publicKey, 
			String location, String digitalSignature){
		
		AccessAudit accessAudit = new AccessAudit();
		
		// lookup private key
		AppDevice appDevice = getAppDeviceDao().getAppDevice(publicKey);
		
		//create computed digital signature from public/private key and data
		String computedDigitalSignature = DigitalSignature.createDigitalSignature(
				resourceUri, publicKey, appDevice.getPrivateKey(), location);
		
		// verify that computed digital signature equals request digital signature
		if(digitalSignature.equals(computedDigitalSignature)){
			
			//get encryption key for resource uri
			getResourceDao().deleteResource(resourceUri, appDevice.getApplication(), 
					appDevice.getDevice(), publicKey);
			
			accessAudit.setAccessResult(AccessAudit.SUCCESS);
		}
		else {
			accessAudit.setAccessResult(AccessAudit.BREACH);
		}
		
		accessAudit.setAccessType("delete encryption key");
		accessAudit.setApplication(appDevice.getApplication());
		accessAudit.setDevice(appDevice.getDevice());
		accessAudit.setResourceUri(resourceUri);
		accessAudit.setLocation(location);
		getAccessAuditDao().saveAccessAudit(accessAudit);

	}
	
	public void deleteResourceById(long id, String apiKey){
		
		Resource resource = getResourceDao().getResourceById(id);
		
		if(resource != null){
			AccessAudit accessAudit = new AccessAudit();
			accessAudit.setAccessResult(AccessAudit.SUCCESS);
			accessAudit.setAccessType("delete encryption key");
			accessAudit.setApplication(resource.getAppDevice().getApplication());
			accessAudit.setDevice(resource.getAppDevice().getDevice());
			accessAudit.setResourceUri(resource.getResourceUri());
			accessAudit.setLocation("Administration website");

			getResourceDao().deleteResourceById(id, apiKey);
			
			getAccessAuditDao().saveAccessAudit(accessAudit);
		}
	}
	
	public long getResourceCount(List<SearchFilter> searchFilters){
		return getResourceDao().getResourceCount(searchFilters);
	}

	public List<ResourceGrid> findResources(List<SearchFilter> searchFilters, 
			String sortIndex, String sortOrder, int firstResult, int maxResults){

		List<Resource> resources = getResourceDao().findResources(searchFilters, sortIndex, sortOrder, firstResult, maxResults);	
		return gridTransformation(resources);
	}
	
	private List<ResourceGrid> gridTransformation(List<Resource> resources){
		
		List<ResourceGrid> gridResources = new ArrayList<ResourceGrid>();
		
		for(Resource rs : resources){
			ResourceGrid rdg = new ResourceGrid();
			rdg.setId(rs.getId());
			rdg.setResourceUri(rs.getResourceUri());
			rdg.setApplication(rs.getAppDevice().getApplication());
			rdg.setDevice(rs.getAppDevice().getDevice());
			gridResources.add(rdg);
		}
		
		return gridResources;
	}
	
	
	private void populateEncryptionKeys(EncryptionKeys encryptionKeys, Resource resource){
		encryptionKeys.setKeyBytes(resource.getKeyBytes());
		encryptionKeys.setKeyBytesLength(resource.getKeyBytesLength());
		encryptionKeys.setIvBytes(resource.getIvBytes());
		encryptionKeys.setIvBytesLength(resource.getIvBytesLength());
		encryptionKeys.setSaltBytes(resource.getSaltBytes());
		encryptionKeys.setSaltBytesLength(resource.getSaltBytesLength());
		encryptionKeys.setIterationCount(resource.getIterationCount());
	}
	
	private void populateBreachEncryptionKeys(EncryptionKeys encryptionKeys){
		
		//generate realistic new encryption keys in order to fool hacker
		byte[] newKeyBytes = KeyUtils.generateKey(16);
		byte[] newIv = KeyUtils.generateIv(16);
		byte[] newSalt = KeyUtils.generateSalt(16);
		int newIterationCount = KeyUtils.generateIterationCount(1024);

		encryptionKeys.setKeyBytes(newKeyBytes);
		encryptionKeys.setKeyBytesLength(16);
		encryptionKeys.setIvBytes(newIv);
		encryptionKeys.setIvBytesLength(16);
		encryptionKeys.setSaltBytes(newSalt);
		encryptionKeys.setSaltBytesLength(16);
		encryptionKeys.setIterationCount(newIterationCount);
	}
	
	public void setVendorDao(VendorDao vendorDao) {
		this.vendorDao = vendorDao;
	}

	public VendorDao getVendorDao() {
		return vendorDao;
	}

	public void setResourceDao(ResourceDao resourceDao) {
		this.resourceDao = resourceDao;
	}

	public ResourceDao getResourceDao() {
		return resourceDao;
	}

	public void setAppDeviceDao(AppDeviceDao appDeviceDao) {
		this.appDeviceDao = appDeviceDao;
	}

	public AppDeviceDao getAppDeviceDao() {
		return appDeviceDao;
	}
	
	public AccessAuditDao getAccessAuditDao() {
		return accessAuditDao;
	}

	public void setAccessAuditDao(AccessAuditDao accessAuditDao) {
		this.accessAuditDao = accessAuditDao;
	}
}
